/**
 * Copyright (c) 2009 Teoti Graphix, LLC See:
 *     http://tkarchitecture.teotigraphix.org
 * 
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 * 
 * @author Michael Schmalle mschmalle@teotigraphix.com
 */
package org.teotigraphix.dx.patterns.manager.core
{

import flash.events.IEventDispatcher;
import flash.utils.Dictionary;

import mx.core.IFactory;

import org.teotigraphix.dx.interfaces.IComponentLayer;
import org.teotigraphix.dx.interfaces.IComponentLayerClient;
import org.teotigraphix.dx.interfaces.IComponentModel;

//[ExcludeClass]

/**
 * @private
 */
public class LayerRegistry
{
	include "../../../core/Version.as"
	
	/**
	 * @private
	 */
	private var layerFactory:IFactory;
	
	/**
	 * @private
	 */
	private var infoFactory:IFactory;
	
	/**
	 * @private
	 */
	private var cache:Dictionary = new Dictionary();
	
	private var infos:Dictionary = new Dictionary();
	
	/**
	 * @private
	 */	
	private var registeredAPIs:Dictionary = new Dictionary();
	
	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------
	
	/**
	 * Constructor.
	 */
	public function LayerRegistry(layerFactory:IFactory, infoFactory:IFactory)
	{
		this.layerFactory = layerFactory;
		this.infoFactory = infoFactory;
	}
	
	//--------------------------------------------------------------------------
	//
	//  Public :: Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 * @private
	 */
	public function createInfo(
		component:IEventDispatcher, client:IComponentLayerClient, api:Class):Object
	{
		var info:Object = createInfoInternal(component, client, api);
		if (!infos[component])
			infos[component] = new Dictionary();
		
		infos[component][api] = info;
		
		return info;
	}
	
	/**
	 * @private
	 */
	public function getInfo(component:IEventDispatcher, api:Class):Object
	{
		if (!infos[component])
			return null;
		
		return infos[component][api];
	}
	
	/**
	 * @private
	 */
	public function getInfoFromModel(model:IComponentModel):Object
	{
		if (!infos[model.component])
			return null;
		
		for each(var o:Object in infos[model.component])
		{
			if (o.model === model)
				return o;
		}
		
		return null;
	}	
	
	/**
	 * @private
	 */
	public function getInfoMap(component:IEventDispatcher):Object
	{
		return infos[component];
	}
	
	/**
	 * @private
	 */
	public function hasInfo(component:IEventDispatcher, api:Class):Boolean
	{
		if (!infos[component])
			return false;
			
		return (infos[component][api] != null);
	}
	
	/**
	 * @private
	 */
	public function removeInfo(component:IEventDispatcher, api:Class):Object
	{
		if (!infos[component])
			return null;
		
		var info:Object = infos[component][api];
		delete infos[component][api];
		return info;
	}	
	
	/**
	 * @private
	 */
	protected function createInfoInternal(
		component:IEventDispatcher, client:IComponentLayerClient, api:Class):Object
	{
		return infoFactory.newInstance();
	}	
	
	
	
	
	//--------------------------------------------------------------------------
	//
	//  Public :: Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 * @private
	 */
	public function createLayer(component:IEventDispatcher):IComponentLayer
	{
		if (cache[component])
			return cache[component];
		
		var layer:IComponentLayer = createComponentLayer();
		if (!layer)
			return null;
		
		cache[component] = layer;
		
		layer.component = component;
		layer.activate(component);
		
		return layer;
	}	
	
	/**
	 * @private
	 */
	public function getLayer(component:IEventDispatcher):IComponentLayer
	{
		var layer:IComponentLayer = cache[component];
		if (layer)
			return layer;
		
		layer = getLayerInternal(component);
		if (layer)
			return layer;
		
		return null;
	}
	
	/**
	 * @private
	 */
	public function hasLayer(component:IEventDispatcher):Boolean
	{
		var layer:IComponentLayer = getLayer(component);
		if (layer)
			return true;
		return false;
	}
	
	/**
	 * @private
	 */
	public function registerClient(
		component:IEventDispatcher, 
		api:Class, client:IComponentLayerClient):void
	{
		var layer:IComponentLayer = getLayer(component);
		if (!layer)
			layer = createLayer(component);
		
		registerClientInternal(layer, api, client);
	}
	
	/**
	 * @private
	 */
	public function removeClient(
		component:IEventDispatcher, api:Class):IComponentLayerClient
	{
		var layer:IComponentLayer = getLayer(component);
		if (!layer)
			throw new Error("layer does not exist");
		
		return removeClientInternal(layer, api);
	}
	
	/**
	 * @private
	 */
	public function hasClient(
		component:IEventDispatcher, api:Class):Boolean
	{
		var layer:IComponentLayer = getLayer(component);
		if (!layer)
			throw new Error("layer does not exist");
		
		return hasClientInternal(layer, api);
	}
	
	/**
	 * @private
	 */
	public function getClient(
		component:IEventDispatcher, api:Class):IComponentLayerClient
	{
		var layer:IComponentLayer = getLayer(component);
		if (!layer)
			throw new Error("layer does not exist");
		
		return getClientInternal(layer, api);
	}
	
	/**
	 * @private
	 */
	public function getClients(
		component:IEventDispatcher):Array
	{
		var layer:IComponentLayer = getLayer(component);
		if (!layer)
			throw new Error("");
		
		return getClientsInternal(layer);
	}
	
	/**
	 * @private
	 */
	public function registerAPI(
		api:Class, clazz:Class):void
	{
		registeredAPIs[api] = clazz;
	}
	
	/**
	 * @private
	 */
	public function createClient(
		component:IEventDispatcher, api:Class):IComponentLayerClient
	{
		var clazz:Class = registeredAPIs[api] as Class;
		if (!clazz)
			return null;
		
		var layer:IComponentLayer;
		
//		if (component is IModelLayerAware)
//			layer = IModelLayerAware(component).behaviorLayer;
		
		if (!layer)
			layer = getLayer(component);
		
		if (!layer)
			layer = createLayer(component);
		
		var client:IComponentLayerClient = new clazz();
		registerClient(component, api, client);
		
		return client;
	}	
	
	//--------------------------------------------------------------------------
	//
	//  Protected :: Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 * @private
	 */
	protected function createComponentLayer():IComponentLayer
	{
		return layerFactory.newInstance() as IComponentLayer;
	}
	
	/**
	 * @private
	 */
	protected function getLayerInternal(component:IEventDispatcher):IComponentLayer
	{	
		return null;
	}	
	
	/**
	 * @private
	 */
	protected function registerClientInternal(
		layer:IComponentLayer, 
		api:Class, client:IComponentLayerClient):void
	{
		throw new Error("Must override registerClientInternal() in subclass");
	}
	
	/**
	 * @private
	 */
	protected function removeClientInternal(
		layer:IComponentLayer, api:Class):IComponentLayerClient
	{
		throw new Error("Must override removeClientInternal() in subclass");
	}
	
	/**
	 * @private
	 */
	protected function hasClientInternal(
		layer:IComponentLayer, api:Class):Boolean
	{
		throw new Error("Must override hasClientInternal() in subclass");
	}
	
	/**
	 * @private
	 */
	protected function getClientInternal(
		layer:IComponentLayer, api:Class):IComponentLayerClient
	{
		throw new Error("Must override getClientInternal() in subclass");
	}
	
	/**
	 * @private
	 */
	protected function getClientsInternal(
		layer:IComponentLayer):Array
	{
		throw new Error("Must override getClientsInternal() in subclass");
	}	
}
}